using System;
using System.Collections.Generic;
using System.Linq;
using Framework.Save;
using LitJson;

namespace GameCore
{
    public enum AreaAccessState
    {
        None, Available, Unavailable, Visited, Current, Missed
    }
    [Serializable]
    public class AreaData : GameData
    {
        public SimpleAdventureRegion region;
        public string areaID;
        public float distanceIndex;
        public AreaAccessState accessState;

        public AreaConfig GetConfig() => PrefabFactory.GetPrefab<AreaConfig>(areaID);
        
        public AreaData(string areaID, SimpleAdventureRegion region)
        {
            this.areaID = areaID;
            this.region = region;
            accessState = AreaAccessState.None;
        }

        public AreaData(JsonData json, SimpleAdventureRegion region)
        {
            this.region = region;
            Deserialize(json);
        }

        public List<AreaData> connectFrom = new();
        public List<AreaData> connectTo = new();
        
        public void AddConnection(AreaData area)
        {
            connectTo.Add(area);
            area.connectFrom.Add(this);
            if (area.distanceIndex < distanceIndex)
                area.distanceIndex = distanceIndex+ 1; 
        }

        public void RecalculateDistanceIndex()
        {
            
        }

        public void UpdateAccessState()
        {
            if (accessState is AreaAccessState.Current or AreaAccessState.Visited) { }
            else if (connectFrom.Any(area => area.accessState == AreaAccessState.Current))
                accessState = AreaAccessState.Available;
            else if (connectFrom.All(area => area.accessState is AreaAccessState.Visited or AreaAccessState.Missed))
                accessState = AreaAccessState.Missed;
            else
                accessState = AreaAccessState.Unavailable;
        }

        private void SetPrevious()
        {
            if (accessState == AreaAccessState.Current)
            {
                accessState = AreaAccessState.Visited;
                return;
            }

            if (connectFrom.All(area => area.accessState is AreaAccessState.Visited or AreaAccessState.Missed))
            {
                accessState = AreaAccessState.Missed;
            }
        }

        public bool AllowTransition(AreaData area)
        {
            return connectTo.Contains(area);
        }

        public override JsonData Serialize()
        {
            var json = JsonUtil.NewObject();
            json["areaID"] = areaID;
            json["distanceIndex"] = distanceIndex;
            json["accessState"] = (int)accessState;
            var jsonConnectTo = JsonUtil.NewArray();
            foreach (var area in connectTo)
                jsonConnectTo.Add(area.areaID);
            json["connectTo"] = jsonConnectTo;
            var jsonConnectFrom = JsonUtil.NewArray();
            foreach (var area in connectFrom)
                jsonConnectFrom.Add(area.areaID);
            json["connectFrom"] = jsonConnectFrom;
            return json;
        }

        public override void Deserialize(JsonData json)
        {
            areaID = (string)json["areaID"];
            distanceIndex = (float)json["distanceIndex"];
            accessState = (AreaAccessState)(int)json["accessState"];
            foreach(JsonData id in json["connectTo"])
                connectTo.Add(region.GetArea((string)id));
            foreach (JsonData id in json["connectFrom"])
                connectFrom.Add(region.GetArea((string)id));
        }
    }
}